//Wifi_time_weather
//连接wifi，获取时间，获取天气
//去掉setup 和 loop

//-----------------------------------------------
// Example for library:
// https://github.com/Bodmer/TJpg_Decoder

// This example is for an ESP32, it renders a Jpeg file that is stored in an array within Flash (program) memory
// see panda.h tab.  The panda image file being ~13Kbytes. The Jpeg decoding is done by one processor core 0 and
// the rendering to TFT by processor 1 (which is normally used to execute the Arduino sketches).

// Single processor core Flash_Jpg sketch on ILI9341 at 40MHz SPI renders the panda Jpeg in 103ms
// The Jpeg image takes 66ms to deocde and 37ms to render to TFT
// This sketch uses both ESP32 processors in parallel so decoding and rendering only takes 66ms
// Processor 1 runs the main sketch, processor 0 runs the Jpeg decoder

#include <Adafruit_GFX.h>    // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735

#include "WiFi.h"
//#include <WiFiManager.h>
#include <time.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include "changeHue.h"

#include "da0.jpg.h"
#include "da1.jpg.h"
#include "da2.jpg.h"
#include "da3.jpg.h"
#include "da4.jpg.h"
#include "da5.jpg.h"
#include "da6.jpg.h"
#include "da7.jpg.h"
#include "da8.jpg.h"
#include "da9.jpg.h"
// Include the jpeg decoder library
#include <TJpg_Decoder.h>

#define firstScreenCS 19
#define secondScreenCS 18
#define ScreensCount 4

//esp32-micropython
// cs,rst 排列，屏幕正面，(最左为1) 
//1,2,3,4
#define TFT_CS     18    //   19   18   17   4
#define TFT_RST    23    //   22   23   5    16  

#define TFT_CS1     19    //   19   18   17   4
#define TFT_RST1    22    //   22   23   5    16  

#define TFT_CS2     18    //   19   18   17   4
#define TFT_RST2    23    //   22   23   5    16  

#define TFT_CS3     17    //   19   18   17   4
#define TFT_RST3    5    //   22   23   5    16  

#define TFT_CS4     4    //   19   18   17   4
#define TFT_RST4    16    //   22   23   5    16  

#define TFT_DC         32
#define TFT_MOSI       25  // Data out
#define TFT_SCLK       26  // Clock out
#define TFT_BL 33 //背光

#define IMGDEF(x) {(uint8_t*)x,sizeof(x)}



typedef struct {
  uint8_t * img;
  int size;
}ImgDefStruct;

ImgDefStruct imgDefs[]={
  IMGDEF(da0_jpg),
  IMGDEF(da1_jpg),
  IMGDEF(da2_jpg),
  IMGDEF(da3_jpg),
  IMGDEF(da4_jpg),
  IMGDEF(da5_jpg),
  IMGDEF(da6_jpg),
  IMGDEF(da7_jpg),
  IMGDEF(da8_jpg),
  IMGDEF(da9_jpg),
};

Adafruit_ST7735 tft1 = Adafruit_ST7735(TFT_CS1, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST1);
Adafruit_ST7735 tft2 = Adafruit_ST7735(TFT_CS2, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST2);
Adafruit_ST7735 tft3 = Adafruit_ST7735(TFT_CS3, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST3);
Adafruit_ST7735 tft4 = Adafruit_ST7735(TFT_CS4, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST4);

int Screens[4]={19,18,17,4};

#include "qing.h"
#include "yin.h"
#include "yun.h"
#include "yu.h"
#include "xue.h"
#include "background.h"
// Include the array



// Include the jpeg decoder library
#include <TJpg_Decoder.h>
#include <PNGdec.h>

// Include the TFT library https://github.com/Bodmer/TFT_eSPI
#include "SPI.h"
#include <TFT_eSPI.h>               // Hardware-specific library
#include "Free_Fonts.h"
#include <U8g2_for_TFT_eSPI.h>

const char* ssid1 = "comicyu_2f";
const char* password1 = "67799706100";

#define NTP1 "ntp1.aliyun.com"
#define NTP2 "ntp2.aliyun.com"
#define NTP3 "ntp3.aliyun.com"

TFT_eSPI tft = TFT_eSPI();          // Invoke custom library
U8g2_for_TFT_eSPI  u8g2;

// Global variables available to BOTH processors 0 and 1
TaskHandle_t Task1;
const uint8_t* arrayName;           // Name of FLASH array containing Jpeg
bool doDecoding = false;            // Mutex flag to start decoding
bool mcuReady = false;              // Mutex flag to indicate an MCU block is ready for rendering
uint16_t mcuBuffer[16*16];          // Buffer to grab a snapshot of decoded MCU block
int32_t mcu_x, mcu_y, mcu_w, mcu_h; // Snapshot of the place to render the MCU

String wea_Temp="30";
String wea_TempRange="24-35";
String wea_TempH="35";
String wea_TempL="24";

String Weather_date="";
String Weather_desc_day="";
String Weather_desc_night="";
String wea_Desc="晴";
String wea_Wind="";
String Weather_reportdate="";

int ColorArr[]={TFT_RED,TFT_ORANGE,TFT_YELLOW,TFT_GREEN,TFT_BLUE,TFT_MAGENTA,TFT_WHITE};
#define ColorArrSize 7
#define TFT_GRAY 0x8410
// This next function will be called by the TJpg_Decoder library during decoding of the jpeg file
// A copy of the decoded MCU block is grabbed for rendering so decoding can then continue while
// the MCU block is rendered on the TFT. Note: This function is called by processor 0
bool mcu_decoded(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
   // Stop further decoding as image is running off bottom of screen
  if ( y >= tft.height() ) return 0;

  while(mcuReady) yield(); // Wait here if rendering of last MCU block to TFT is still in progress

  memcpy(mcuBuffer, bitmap, 16*16*2); // Grab a copy of the MCU block image
  mcu_x = x;                          // Grab postion and size of MCU block
  mcu_y = y;
  mcu_w = w;
  mcu_h = h;
  mcuReady = true; // Flag to tell processor 1 that rendering of MCU can start

  // Return 1 to decode next Jpeg MCU block
  return 1;
}

// This is the task that runs on processor 0 (Arduino sketch runs on processor 1)
// It decodes the Jpeg image
void decodeJpg(void* p) {
  // This is an infinite loop, effectively the same as the normal sketch loop()
  // but this function and loop is running on processor 0
  for(;;) {
    // Decode the Jpeg image
    if (doDecoding) { // Only start decoding if main sketch sets this flag
      TJpgDec.drawJpg(0, 0, arrayName, sizeof(background)); // Runs until complete image decoded
      doDecoding = false; // Set mutex false to indicate decoding has ended
    }
    // Must yield in this loop
    yield();
  }
}

void enableAll()
{
  for(int i=0;i<ScreensCount;i++)
  {
    digitalWrite(Screens[i], LOW);  
  }
}
void disableAll()
{
  for(int i=0;i<ScreensCount;i++)
  {
    digitalWrite(Screens[i], HIGH);  
  }
}

void enableDisp(int i)
{
  digitalWrite(Screens[i], LOW);  
}

void disableDisp(int i)
{
  digitalWrite(Screens[i], HIGH);  
}

void setup()
{
  setupBasic();
  setup_wifi_time_weather();
}

void setupBasic() {
  Serial.begin(115200);
  delay(200);
  Serial.println("Esp32 st7735 4*1.8 inch displays by Bruce Yu.");
  Serial.println("--------------------------------------------------");
  //ColorArrswapRB();
  initScreens();
  delay(1000);
  disableAll();
  enableAll();
// We need to 'init' both displays
  // at the same time. so set both cs pins low
    
  tft.init();
  tft.setTextSize(1);
  
  // Set both cs pins HIGH, or 'inactive'
  //digitalWrite(firstScreenCS, HIGH);
  //digitalWrite(secondScreenCS, HIGH);

  //初始化jpg解码器  

   // The jpeg image can be scaled down by a factor of 1, 2, 4, or 8
  TJpgDec.setJpgScale(1);
  // The colour byte order can be swapped by the decoder
  // using TJpgDec.setSwapBytes(true); or by the TFT_eSPI library:
  tft.setSwapBytes(true);
  // The decoder must be given the exact name of the rendering function above
  TJpgDec.setCallback(tft_output);
  disableAll();
}

void setup_wifi_time_weather()
{
  // //Create task decodeJpg to run on processor 0 to decode a Jpeg
  // xTaskCreatePinnedToCore(decodeJpg, "decodeJpg", 10000, NULL, 0, NULL, 0);

  // // Initialise the TFT
  // tft.begin();
  // u8g2.begin(tft);
  // tft.setTextColor(0xFFFF, 0x0000);
  // tft.fillScreen(TFT_BLACK);
  // tft.setRotation(1);
  // // The jpeg image can be scaled by a factor of 1, 2, 4, or 8
  // TJpgDec.setJpgScale(1);
  // // The byte order can be swapped (set true for TFT_eSPI)
  // TJpgDec.setSwapBytes(true);
  // // The decoder must be given the exact name of the mcu buffer function above
  // TJpgDec.setCallback(mcu_decoded);
  enableDisp(0);
  connectWifi();
  configTime(8*3600,0,NTP1,NTP2,NTP3);
  struct tm timeinfo;
  while(!getLocalTime(&timeinfo, 3000))
  {
    delay(1000); 
    printBoth(".");   
  }
  printBoth("Got time.");
  printBoth("Getting weather...");
  getWeather();
  disableDisp(0);
  clearAll();
  //tft.fillScreen(TFT_RED);
  //showBackground();
  //loop();
}

void clear()
{
  tft.setCursor(0,0);
  tft.fillScreen(TFT_BLACK);
  Serial.println("");
}

void clear(int disp)
{
  enableDisp(disp);clear();disableDisp(disp);
}

void clearAll()
{
  enableDisp(0);clear();disableDisp(0);
  enableDisp(1);clear();disableDisp(1);
  enableDisp(2);clear();disableDisp(2);
  enableDisp(3);clear();disableDisp(3);
}

void connectWifi()
{
  int ic=0;
  WiFi.mode(WIFI_STA);
  WiFi.disconnect();

  while (WiFi.status() != WL_CONNECTED)
  {
    printBoth("Connect to Wifi:");
    // printBoth(ssid);
    // ic=0;
    // WiFi.begin(ssid, password);
    // delay(1000);
    // while (WiFi.status() != WL_CONNECTED && ic<20) 
    // {  
    //   printBoth("-");
    //   delay(1000);
    //   ic++;
    // }
    
    if(WiFi.status() != WL_CONNECTED)
    {
      ic=0;

      printBoth("Change to Wifi:");
      printBoth(ssid1);
      WiFi.begin(ssid1, password1);
      delay(1000);
      while (WiFi.status() != WL_CONNECTED && ic<20) {  
        printBoth("-");
        delay(1000);
        ic++;
      }
      clear();
    }
  }
    printBoth("WiFi connected!");
}


void loop()
{
  current_hue++;
  current_hue%=360;
  loop_time_weather();  
  delay(1000);
}

int icc=0;
String weekdays[7]={"日","一","二","三","四","五","六"};
void loop_time_weather()
{
  // Wait before drawing again

  time_t now;
  now=time(NULL);
  struct tm * ltm=localtime(&now);

  //showBackground();
  int y=108;
  if((icc%600)==0)
  {
    getWeather();

  }
  icc=(icc+1)%1800;

  showTime(ltm);
  
//   //hour
//   tft.setCursor(45,y);
//   tft.setFreeFont(FSS24);
//   tft.setTextSize(2);
//   tft.setTextColor(TFT_BLACK);
//   tft.printf("%02d",ltm->tm_hour);
//   tft.setCursor(168,y);
//   tft.printf("%02d",ltm->tm_min);

// //天气
//   showWeatherImg();
//   tft.setCursor(215,170);
//   tft.setFreeFont(FSS18);
//   tft.setTextSize(1);
//   tft.setTextColor(TFT_WHITE);
//   tft.print(wea_Temp);
//   tft.setFreeFont(FSS9);

//   u8g2.setFont(u8g2_font_wqy16_t_gb2312a);
//   u8g2.setForegroundColor(TFT_WHITE);  //字的颜色

//   u8g2.setCursor(260,155);
//   u8g2.print(wea_TempH);
//   u8g2.setCursor(260,170);
//   u8g2.print(wea_TempL);
  
//   u8g2.setCursor(40,160);
  
//   u8g2.print(wea_Desc);
//   char buffer[50];
//   u8g2.setCursor(160,230);
//   sprintf(buffer,"%d-%02d-%02d %s",ltm->tm_year+1900,ltm->tm_mon+1,ltm->tm_mday,weekdays[ltm->tm_wday]);
//   u8g2.print(buffer);

}


void drawImg(const uint8_t*img,int size,int x=0,int y=0)
{
  // Must use startWrite first so TFT chip select stays low during DMA and SPI channel settings remain configured
  tft.startWrite();
  // Draw the image, top left at 0,0 - DMA request is handled in the call-back tft_output() in this sketch
  TJpgDec.drawJpg(x, y, img,size);
  // Must use endWrite to release the TFT chip select and release the SPI channel
  tft.endWrite();
}


void drawDigit(int disp,int digit,int x=10,int y=0)
{
  enableDisp(disp);  
  drawImg(imgDefs[digit].img,imgDefs[digit].size,x,y);
  disableDisp(disp);  
}


int lasth=-1;
int lastm=-1;

void showTime(struct tm * ltm)
{

  int h_tens=ltm->tm_hour/10;
  int h_units=ltm->tm_hour%10;
  int m_tens = ltm->tm_min/10;
  int m_units = ltm->tm_min%10;
  if(lasth!=ltm->tm_hour)
  {
    clear(0);
    clear(1);
    drawDigit(0,h_tens);
    drawDigit(1,h_units);
    lasth=ltm->tm_hour;
    showWeatherImg();  
    showWeatherText();
  }

  if(lastm!=ltm->tm_min)
  {
    drawDigit(2,m_tens);
    drawDigit(3,m_units);
    lastm=ltm->tm_min;
    showWeatherImg();  
    showWeatherText();
  }

  showSeconds(ltm->tm_sec);
  
}

//void drawDigit(int disp,int digit){}

#define TFT_WIDTH 128
#define SEC_Y TFT_HEIGHT-2
#define SEC_H 2
void showSeconds(int sec)
{
  int disp = sec/15;
  int width = ((sec % 15)+1) * TFT_WIDTH / 15;
  //清除Sec
  if(sec==0 || sec==1)
  {
    for(int i=0;i<=3;i++)
    {
      enableDisp(i);  
      tft.fillRect(0,SEC_Y,TFT_WIDTH,SEC_H,TFT_GRAY); //ColorArr[2]);  
      disableDisp(i);
    }  
  }

  #define COLOR_Hline TFT_WHITE //0xFFFF   //TFT_MAGENTA

  // for(int i=0;i<disp;i++)
  // {
  //   enableDisp(i);  
  //   tft.fillRect(0,SEC_Y,TFT_WIDTH,SEC_H,TFT_BLUE);  
  //   disableDisp(i);
  // }
  enableDisp(disp);
  tft.fillRect(0,SEC_Y,width,SEC_H,TFT_BLUE);
  disableDisp(disp);
}

void printBoth(const char* str)
{
  Serial.print(str);
  tft.print(str);
}

String getWeatherReq(String base)
{
  String result="";
  Serial.println("Getting weather ...");
  String url = "https://restapi.amap.com/v3/weather/weatherInfo?parameters&key=e094cccaa9f94c734344ab015d8b4ec6&city=110000&extensions="+base+"&output=JSON";
  HTTPClient http;
  http.begin(url);
  int httpCode=http.GET();
  if(httpCode>0)
    result = http.getString();
  Serial.println(result);
  return result;
}

void getWeather()
{
      String wea = getWeatherReq("base");
      if(wea.length()>0)
      {
        StaticJsonDocument<300> doc;
        deserializeJson(doc,wea);
      
        wea_Desc=String((const char *)doc["lives"][0]["weather"]);
        wea_Temp=String((const char *)doc["lives"][0]["temperature"]);
        wea_Wind=String((const char *)doc["lives"][0]["winddirection"])+String((const char *)doc["lives"][0]["windpower"]);
        Weather_reportdate=String((const char *)doc["lives"][0]["reporttime"]).substring(0,10);
      }
      
      if(Weather_date!=Weather_reportdate)
      {
        wea = getWeatherReq("all");
        if(wea.length()>0)
        {
          StaticJsonDocument<1500> doc;
          deserializeJson(doc,wea);
          Serial.println(wea);
          Weather_date=String((const char *)doc["forecasts"][0]["casts"][0]["date"]);
          Weather_desc_day=String((const char *)doc["forecasts"][0]["casts"][0]["dayweather"]);
          Weather_desc_night=String((const char *)doc["forecasts"][0]["casts"][0]["nightweather"]);
          wea_TempH=String((const char *)doc["forecasts"][0]["casts"][0]["daytemp"]);
          wea_TempL=String((const char *)doc["forecasts"][0]["casts"][0]["nighttemp"]);
        }
      }
}

//#define SHOWPNG(img,x,y) nothingToDo()
#define SHOWPNG(img,x,y) showPng(((uint8_t*)img),sizeof(img),(x),(y))

void nothingToDo(){}


void showWeatherText()
{
  int y=145;
  enableDisp(0);
  tft.setTextColor(TFT_GREEN);//(0xF418);  //#888-8410   
  tft.setTextSize(2);
  tft.setCursor(100,y);
  tft.print(wea_Temp);
  disableDisp(0);
  enableDisp(1);
  tft.setTextSize(2);
  //tft.setCursor(0,y);
  //tft.print(wea_TempRange);
  tft.setCursor(0,y);
  tft.print(Weather_date);
  disableDisp(1);
}

void showWeatherImg()
{
    int weax=0;
    int weay=100;
    
    enableDisp(0);
    if(wea_Desc.indexOf("晴")>=0)
      SHOWPNG(qing,weax,weay);
    else if (wea_Desc.indexOf("阴")>=0)
      SHOWPNG(yin,weax,weay);
    else if (wea_Desc.indexOf("雨")>=0)
      SHOWPNG(yu,weax,weay);
    else if (wea_Desc.indexOf("雪")>=0)
      SHOWPNG(xue,weax,weay);
    else
      SHOWPNG(yun,weax,weay);
    disableDisp(0);
}

//-------------------------------------------------
PNG png;
#define MAX_IMAGE_WIDTH 128
// =========================================v==========================================
//                                      pngDraw
// ====================================================================================
// This next function will be called during decoding of the png file to
// render each image line to the TFT.  If you use a different TFT library
// you will need to adapt this function to suit.
// Callback function to draw pixels to the display

typedef struct my_private_struct
{
  int xoff, yoff; // corner offset
} PRIVATE;

//void showPng(uint8_t* img,int size,int x,int y){}

void pngDraw(PNGDRAW *pDraw) {
  uint16_t lineBuffer[MAX_IMAGE_WIDTH];
  uint8_t  maskBuffer[1 + MAX_IMAGE_WIDTH / 8];  // Mask buffer
  png.getLineAsRGB565(pDraw, lineBuffer, PNG_RGB565_BIG_ENDIAN, 0xffffffff);
  PRIVATE *pPriv = (PRIVATE *)pDraw->pUser;
  if (png.getAlphaMask(pDraw, maskBuffer, 255)) {
    tft.pushMaskedImage(pPriv->xoff, pPriv->yoff + pDraw->y, pDraw->iWidth, 1, lineBuffer,maskBuffer);
  }
}


void showPng(uint8_t* img,int size,int x,int y)
{
  PRIVATE priv;
  int rc, i = 0;
  rc = png.openRAM(img,size, pngDraw);
  if (rc == PNG_SUCCESS) {
      //Serial.printf("image specs: (%d x %d), %d bpp, pixel type: %d\n", png.getWidth(), png.getHeight(), png.getBpp(), png.getPixelType());
      priv.xoff = x;
      priv.yoff = y;
      rc = png.decode((void *)&priv, 0);
      png.close();
  }
}

void ColorArrswapRB()
{
  for(int i=0;i<ColorArrSize;i++)
    ColorArr[i]=swapRB(ColorArr[i]);
}

uint16_t swapRB(uint16_t color) {
    // 首先，我们需要分别提取R、G、B分量
    uint16_t r = (color >> 11) & 0x1F; // 提取红色分量
    uint16_t g = (color >> 5) & 0x3F;  // 提取绿色分量
    uint16_t b = color & 0x1F;         // 提取蓝色分量

    // 然后，交换R和B分量
    uint16_t rb = r;
    r = b;
    b = rb;

    // 最后，重新组合R、G、B分量
    color = (r << 11) | (g << 5) | b;
    return color;
}


void initScreens()
{
  tft1.initR(INITR_BLACKTAB);      // Init ST7735S chip, black tab
  //tft1.fillScreen(ST77XX_BLACK);
  tft2.initR(INITR_BLACKTAB);
  //tft2.fillScreen(ST77XX_BLACK);
  tft3.initR(INITR_BLACKTAB);
  //tft3.fillScreen(ST77XX_BLACK);
  tft4.initR(INITR_BLACKTAB);
  //tft4.fillScreen(ST77XX_BLACK);
}


bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap)
{
   // Stop further decoding as image is running off bottom of screen
  if ( y >= tft.height() ) return 0;
  // STM32F767 processor takes 43ms just to decode (and not draw) jpeg (-Os compile option)
  // Total time to decode and also draw to TFT:
  // SPI 54MHz=71ms, with DMA 50ms, 71-43 = 28ms spent drawing, so DMA is complete before next MCU block is ready
  // Apparent performance benefit of DMA = 71/50 = 42%, 50 - 43 = 7ms lost elsewhere
  // SPI 27MHz=95ms, with DMA 52ms. 95-43 = 52ms spent drawing, so DMA is *just* complete before next MCU block is ready!
  // Apparent performance benefit of DMA = 95/52 = 83%, 52 - 43 = 9ms lost elsewhere
// #ifdef USE_DMA
//   // Double buffering is used, the bitmap is copied to the buffer by pushImageDMA() the
//   // bitmap can then be updated by the jpeg decoder while DMA is in progress
//   if (dmaBufferSel) dmaBufferPtr = dmaBuffer2;
//   else dmaBufferPtr = dmaBuffer1;
//   dmaBufferSel = !dmaBufferSel; // Toggle buffer selection
//   //  pushImageDMA() will clip the image block at screen boundaries before initiating DMA
//   tft.pushImageDMA(x, y, w, h, bitmap, dmaBufferPtr); // Initiate DMA - blocking only if last DMA is not complete
//   // The DMA transfer of image block to the TFT is now in progress...
// #else
  // Non-DMA blocking alternative
  
  //changeHue(bitmap,w,h);
  tft.pushImage(x, y, w, h, bitmap);  // Blocking, so only returns when image block is drawn
// #endif
  // Return 1 to decode next block.
  return 1;
}

